home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK2.toast / Development Kits (Disc 2) / QuickDraw GX / Programming Stuff / Sample Code / Printing Samples / Printer Drivers… / LaserWriter--custom dialogs / Generic LaserWriter.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-10  |  34.7 KB  |  1,205 lines  |  [TEXT/MPS ]

  1. /*-----------------------------------------------------------------------------
  2.  
  3.     Generic LaserWriter.c
  4.     
  5.      This part of the driver demonstrates how to implement custom
  6.      old-API dialogs for a PostScript driver.
  7.         
  8.      12/18/93 - dmh - Removed IIg-dependencies for b3.
  9.       9/13/93 - dmh - Updated for the b2 seed.
  10.       9/14/93 - dmh - Disabled the PostScript log which was created in
  11.                       the Extensions folder.
  12.       2/11/94 - dmh - Added custom old-API dialogs.
  13.       3/08/94 - lll - Fixed DriverConvertPrintRecordTo so that Orientation is handled correctly
  14.       4/20/94 - dmh - Aaaaah!  Forgotten style dialog raises havok.  News at 11.
  15.       6/20/94 - dmh - Removed the PS file log code, for clarity.
  16.       8/28/94 - dmh - Finalized for SDK.
  17.  
  18.     © 1991-1994 Apple Computer Inc.
  19.     
  20. -----------------------------------------------------------------------------*/
  21.  
  22. #include <PrintingDrivers.h>
  23. #include <GXExceptions.h>
  24. #include <TextUtils.h>
  25. #include <Packages.h>
  26. #include <Resources.h>
  27. #include <Errors.h>
  28. #include <StandardFile.h>
  29.  
  30. #define kDeviceByte        (char) 0x88        /* Our wDev ID */
  31.  
  32. #define kJob_CopiesEditText            5
  33. #define kJob_AllPagesRadio            7
  34. #define kJob_FromPageRadio            8
  35. #define kJob_FromPageEditText        9
  36. #define kJob_ToPageEditText            11
  37. #define kJob_AutoFeedRadio            12
  38. #define kJob_ManualFeedRadio        13
  39. #define kJob_PrintToDiskCheckBox    15
  40. #define kJob_lineFrill_1            16
  41. #define kJob_lineFrill_2            17
  42.  
  43. #define kStl_lineFrill_1            4
  44. #define kStl_lineFrill_2            5
  45.  
  46. extern long A5Size (void);
  47. extern void A5Init (void *);
  48.  
  49. void        InitJobDialogControls(DialogPtr theDialog, THPrint thePrintHdl);
  50. OSErr        PrepForFileWriting(void);
  51. pascal void    DriverJobItemHandler(DialogPtr theDialog, short item);
  52. pascal Boolean DriverJobFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *itemHit);
  53. void        ToggleControl(void **itemH);
  54. void        SetTheControlValue(void **itemH, short desiredValue);
  55. void        UpdateJobDlgPrintRec(DialogPtr theDialog);
  56. OSErr        StoreJobCollectionItem(void *collectItem, long collectSize,
  57.                                    OSType collectType, long collectID);
  58.  
  59. long GetCurrentA5(void) = 0x200D;    // move.l a5, d0
  60.  
  61.  
  62. // A structure for our context data.
  63.  
  64. typedef struct MyContextDataRec {
  65.     long        appA5;
  66.     Boolean        inStyleDialog;
  67. } MyContextDataRec, *MyContextDataPtr, **MyContextDataHdl;
  68.  
  69.  
  70.  
  71. /*    ______________________________________________________________
  72.  
  73.     DriverInitialize -
  74.     
  75.     This routine is a total override of the gxInitialize
  76.     message. In here, we store the application's A5 reference
  77.     for later, and set up our driver's A5 world, so we can
  78.     access global data.
  79.     
  80.     WHY THIS IS IMPORTANT:
  81.     
  82.     If you do not have the application's A5 reference, you won't
  83.     be able to support application callbacks, which occur when
  84.     you totally (and properly) override PrDlgMain.  If you're
  85.     going to perform a total override of PrDlgMain, you need
  86.     the app's A5 value.  This is probably the best place to get
  87.     it.  Note that we get and store the A5 reference in our
  88.     instance context BEFORE calling NewMessageGlobals.  Once
  89.     NewMessageGlobals is called, the app's A5 reference is
  90.     replaced with ours.
  91.  
  92.     ______________________________________________________________    */
  93.  
  94. OSErr DriverInitialize(void)
  95. {
  96.     OSErr                err;
  97.     MyContextDataHdl    contextData;
  98.     
  99.     contextData = (MyContextDataHdl) NewHandleSys(sizeof(MyContextDataRec));
  100.     nrequire(err = MemError(), NewHandleSys_Failed);
  101.  
  102.     (*contextData)->appA5 = GetCurrentA5();
  103.     (*contextData)->inStyleDialog = false;
  104.     SetMessageHandlerInstanceContext(contextData);
  105.  
  106.     err = NewMessageGlobals(A5Size(), A5Init);
  107.  
  108. NewHandleSys_Failed:    
  109.     return err;
  110. }
  111.  
  112.  
  113. /*    ______________________________________________________________
  114.  
  115.     DriverShutDown -
  116.     
  117.     This routine is a total override of the gxShutDown
  118.     message.
  119.     
  120.     WHY THIS IS IMPORTANT:
  121.     
  122.     It cleans up after our DriverInitialize override.
  123.  
  124.     ______________________________________________________________    */
  125.  
  126. OSErr DriverShutDown(void)
  127. {
  128.     MyContextDataHdl    contextData;
  129.     
  130.     contextData = GetMessageHandlerInstanceContext();
  131.  
  132.     if (contextData != nil)
  133.         DisposHandle((Handle) contextData);
  134.  
  135.     DisposeMessageGlobals();
  136.     return noErr;
  137. }
  138.  
  139.  
  140. /*    ______________________________________________________________
  141.  
  142.     DriverPrDlgMain -
  143.     
  144.     This routine is a total override of the gxPrDlgMain message in
  145.     the Job dialog case, and a partial override in the Style
  146.     dialog case. In here, we perform displaying and handling of
  147.     the old-API print (Job and Style) dialogs.
  148.  
  149.     WHY THIS IS IMPORTANT:
  150.     
  151.     If you need to customize the old-API dialogs for a PostScript
  152.     driver, you cannot simply add a 'dctl' and 'DITL' resource
  153.     like you can for a raster or vector driver.  The default
  154.     PostScript implementation will ignore your resources.  Also,
  155.     because of everything the default PostScript implementation
  156.     does with its own dctl behind the scenes, you cannot partially
  157.     override this message, switch in your dialog somehow and
  158.     live to tell about it.
  159.     
  160.     You must completely override this message if you're doing
  161.     custom old-API dialogs for a PostScript driver (As in the Job
  162.     dialog case for this driver.  And, you need the application's
  163.     A5 reference to pull this off. See the DriverInitialize routine
  164.     above for details on getting that reference.
  165.  
  166.     ______________________________________________________________    */
  167.  
  168. OSErr DriverPrDlgMain(THPrint hPrint, PDlgInitProcPtr pDlgInit,
  169.                       Boolean *okPressed)
  170. {
  171.     OSErr               err = noErr;
  172.     TPPrDlg             prDlgPtr;
  173.     GrafPtr                oldPort;
  174.     short                  itemHit;
  175.     long                oldA5, appA5;
  176.      MyContextDataHdl    contextData;
  177.  
  178. /*
  179.     Retrieve the data we stored in our instance context.
  180.     This data contains a reference to the app's A5 world,
  181.     and a boolean indicating wether we're putting up the
  182.     Style or Job dialogs.  If we're being called for the
  183.     Style dialog, simply forward the message and return.
  184.     That's because this driver doesn't modify the default
  185.     PS style dialog.
  186. */
  187.  
  188.     contextData = GetMessageHandlerInstanceContext();
  189.  
  190.     if ((*contextData)->inStyleDialog)
  191.     {
  192.         err = Forward_GXPrDlgMain(hPrint, pDlgInit, okPressed);
  193.         nrequire(true, ClearContextFlag);
  194.     }
  195.  
  196.  
  197. /*
  198.     Retrieve the application's A5 reference that we stored
  199.     in our instance context.  If it's valid (non-nil),
  200.     save our A5, switch in the app's, and call the initProc to
  201.     get the print dialog pointer.
  202.  
  203.     If the application added any items to our dialog, it will
  204.     do some initialization here, and may need it's A5 to be
  205.     valid to do so.  After we return, restore our A5 value so
  206.     that we can access our globals and so forth.
  207. */
  208.  
  209.     appA5 = (*contextData)->appA5;
  210.   
  211.     if (appA5) 
  212.         oldA5 = SetA5(appA5);
  213.  
  214.     prDlgPtr = (*pDlgInit)(hPrint);
  215.  
  216.     if (appA5) 
  217.         SetA5(oldA5);
  218.  
  219.     require(prDlgPtr, DialogInit);
  220.  
  221.   
  222. /*
  223.     Now that we have the print dialog, save the current port,
  224.     and show the dialog.
  225. */
  226.     
  227.     GetPort(&oldPort);
  228.     ShowWindow((WindowPtr) prDlgPtr);
  229.  
  230.  
  231. /*
  232.     Here's the guts of this routine.  We simply use ModalDialog
  233.     to handle things.  Notice that we restore the app's A5
  234.     register around the ModalDialog procedure.  Again, this is
  235.     a likely place for an app to handle appended dialog items,
  236.     and it may require a valid A5 to do so.
  237.  
  238.     Take a look at DriverPrJobInit or DriverPrStlInit to see
  239.     how we set up both the filterProc and the itemProc before
  240.     we ever get here.  You may be wondering how an application
  241.     could get control since we're doing that.  The app would
  242.     replace our filter and item procs with its own, and then
  243.     pass control back to us as needed (for handling our items).
  244.     
  245.     Anyway, we keep looping until the fDone flag is set.
  246. */
  247.  
  248.    do
  249.    {
  250.        SetPort((GrafPtr) prDlgPtr);
  251.      
  252.        if (appA5) 
  253.            oldA5 = SetA5(appA5);
  254.  
  255.        ModalDialog(prDlgPtr->pFltrProc, &itemHit);
  256.        prDlgPtr->pItemProc((DialogPtr) prDlgPtr, itemHit);
  257.     
  258.        if (appA5)
  259.             SetA5(oldA5);
  260.      
  261.    } while (!prDlgPtr->fDone);
  262.  
  263.    *okPressed = prDlgPtr->fDoIt;
  264.    SetPort(oldPort);
  265.  
  266. /*
  267.     We're all done with the grunt work.  Now clean up after
  268.     ourselves and validate the dialog if the user pressed OK.
  269.     Finally, clear the idle proc to make sure that an old
  270.     one that was saved in the document doesn't get used by
  271.     mistake!
  272. */
  273.  
  274.     CloseDialog((DialogPtr) prDlgPtr);
  275.     DisposHandle(((DialogPeek) prDlgPtr)->items);
  276.     DisposePtr((Ptr) prDlgPtr);
  277.  
  278. /*
  279.     If the user pressed OK, call PrValidate.
  280. */
  281.  
  282.     if (*okPressed)
  283.     {
  284.         PrValidate(hPrint);
  285.         (*hPrint)->prJob.pIdleProc = nil;
  286.     }
  287.  
  288. ClearContextFlag:
  289.     contextData = GetMessageHandlerInstanceContext();
  290.     (*contextData)->inStyleDialog = false;
  291.     SetMessageHandlerInstanceContext(contextData);
  292.  
  293. DialogInit:
  294.     return err;
  295. }
  296.  
  297.  
  298. /*    ______________________________________________________________
  299.  
  300.     DriverPrStlInit -
  301.     
  302.     This routine is a partial override for the gxPrStlInit message.
  303.  
  304.     WHY THIS IS IMPORTANT:
  305.     
  306.     This is where we set our flag that tells PrDlgMain that we're
  307.     putting up the Style dialog.
  308.  
  309.     ______________________________________________________________    */
  310.  
  311. OSErr DriverPrStlInit(THPrint aPrintHdl, TPPrDlg *aPrDlg)
  312. {
  313.      MyContextDataHdl    contextData;
  314.  
  315.     contextData = GetMessageHandlerInstanceContext();
  316.     (*contextData)->inStyleDialog = true;
  317.     SetMessageHandlerInstanceContext(contextData);
  318.  
  319.     return Forward_GXPrStlInit(aPrintHdl, aPrDlg);
  320. }
  321.  
  322.  
  323. /*    ______________________________________________________________
  324.  
  325.     DriverPrJobInit -
  326.     
  327.     This routine is a total override for the gxPrJobInit message.
  328.     In here, we create our print dialog pointer, and initialize
  329.     it and the print handle as we see fit.
  330.  
  331.     WHY THIS IS IMPORTANT:
  332.     
  333.     This is where we install our ModalDialog filter proc and our
  334.     job item handler.
  335.     
  336.     You must completely override this message if you're changing
  337.     the default old-API dialogs in any way. If you try to perform
  338.     a partial override, the default PS implementation is going to
  339.     give you a good swift kick.
  340.  
  341.     ______________________________________________________________    */
  342.  
  343. OSErr DriverPrJobInit(THPrint aPrintHdl, TPPrDlg *aPrDlg)
  344. {
  345.     OSErr               err = noErr;
  346.     short                oldResFile = CurResFile();
  347.      MyContextDataHdl    contextData;
  348.  
  349. /*
  350.    Indicate to our PrDlgMain override that we are displaying
  351.    the Print dialog.
  352. */
  353.  
  354.     contextData = GetMessageHandlerInstanceContext();
  355.     (*contextData)->inStyleDialog = false;
  356.     SetMessageHandlerInstanceContext(contextData);
  357.  
  358.  
  359. /*
  360.    Validate our print record, then create our dialog pointer.
  361. */
  362.  
  363.    PrValidate(aPrintHdl);
  364.    *aPrDlg = (TPPrDlg) NewPtrClear(sizeof(TPrDlg));
  365.    require_action(*aPrDlg, NewPtrClear_Failed, err = MemError(););
  366.  
  367.  
  368. /*
  369.    This next line forces manual feed mode off for our driver.
  370.    The rest is standard print dialog setup stuff.
  371. */
  372.  
  373.    (*aPrintHdl)->prStl.feed = 2;
  374.  
  375.    (*aPrDlg)->pFltrProc = (ModalFilterProcPtr) DriverJobFilterProc;
  376.    (*aPrDlg)->pItemProc = (PItemProcPtr) DriverJobItemHandler;
  377.    (*aPrDlg)->hPrintUsr = aPrintHdl;
  378.    (*aPrDlg)->fDoIt = false;
  379.    (*aPrDlg)->fDone = false;
  380.  
  381.  
  382. /*
  383.    Load our dialog, using the dialog pointer we just created.
  384.    Initialize our dialog's controls so they reflect what's
  385.    currently in our print record.  You need the drivers resfile
  386.    or the pict in the print dialog will be scrunged in some apps
  387. */
  388.  
  389.     UseResFile(GXGetMessageHandlerResFile());
  390.     GetNewDialog(gxJobDialogResID, *aPrDlg, (WindowPtr) -1);
  391.     InitJobDialogControls((DialogPtr) *aPrDlg, aPrintHdl);
  392.  
  393.     UseResFile(oldResFile);
  394.  
  395. NewPtrClear_Failed:
  396.    return err;
  397. }
  398.  
  399.  
  400. /*    ______________________________________________________________
  401.  
  402.     DriverJobFilterProc -
  403.     
  404.     This routine is our ModalDialog filter proc for our Print
  405.     dialog.  It gets called during the PrDlgMain cycle.
  406.  
  407.     WHY THIS IS IMPORTANT:
  408.     
  409.     This is where we can handle update events for user items, and
  410.     that sort of thing.
  411.  
  412.     ______________________________________________________________    */
  413.  
  414. pascal Boolean DriverJobFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *itemHit)
  415. {
  416.     short        itemKind;
  417.     Handle        itemHdl;
  418.     Rect        itemRect;
  419.  
  420. #pragma unused(itemHit);
  421.  
  422. /*
  423.     If there's an update event, draw a border around the default
  424.     button, and draw our dialog's line frills.
  425. */
  426.  
  427.     switch (theEvent->what)
  428.     {
  429.         case updateEvt:
  430.         
  431.             SetPort(theDialog);
  432.             GetDItem(theDialog, ok, &itemKind, &itemHdl, &itemRect);
  433.             PenSize(3, 3);
  434.             InsetRect(&itemRect, -4, -4);
  435.             FrameRoundRect(&itemRect, 16, 16);
  436.             PenSize(1, 1);
  437.  
  438.             GetDItem(theDialog, kJob_lineFrill_1, &itemKind, &itemHdl, &itemRect);
  439.             MoveTo(itemRect.left, itemRect.top);
  440.             LineTo(itemRect.right, itemRect.top);
  441.             GetDItem(theDialog, kJob_lineFrill_2, &itemKind, &itemHdl, &itemRect);
  442.             MoveTo(itemRect.left, itemRect.top);
  443.             LineTo(itemRect.right, itemRect.top);
  444.             break;
  445.     }
  446.     
  447.     return false;
  448. }
  449.  
  450.  
  451. /*    ______________________________________________________________
  452.  
  453.     DriverJobItemHandler -
  454.     
  455.     This routine handles item hits in our job dialog.  It is
  456.     installed in the print dialog by DriverPrJobInit, and will be
  457.     called during the PrDlgMain cycle.
  458.  
  459.     WHY THIS IS IMPORTANT:
  460.     
  461.     This is the routine that handles all hits in our Print dialog.
  462.  
  463.     ______________________________________________________________    */
  464.  
  465. pascal void DriverJobItemHandler(DialogPtr theDialog, short item)
  466. {
  467.     OSErr        err = noErr;
  468.     TPPrDlg        thePrDlg = (TPPrDlg) theDialog;
  469.     short        itemKind;
  470.     Handle        itemHdl;
  471.     Rect        itemRect;
  472.  
  473.     switch (item)
  474.     {
  475. /*
  476.     Was OK pressed?  See if we're printing to disk.  If so, prompt
  477.     the user for a location.  If they cancel, continue with the print
  478.     dialog. Otherwise, update the print record to reflect the current
  479.     dialog settings, and signal that we're done and we're going to
  480.     "do it."
  481.     
  482.     Note that PrepForFileWriting adds our "print to file" collection
  483.     items to the current job for us.
  484. */
  485.         case ok:
  486.             GetDItem(theDialog, kJob_PrintToDiskCheckBox, &itemKind, &itemHdl, &itemRect);
  487.             if (GetCtlValue((ControlHandle) itemHdl) != 0)
  488.                 err = PrepForFileWriting();
  489.  
  490.             if (!err)
  491.             {
  492.                 thePrDlg->fDoIt = true;
  493.                 thePrDlg->fDone = true;
  494.                 UpdateJobDlgPrintRec(theDialog);
  495.             }
  496.             break;
  497. /*
  498.     Was Cancel pressed?  We're done but we're not "doing it."
  499. */
  500.         case cancel:
  501.             thePrDlg->fDoIt = false;
  502.             thePrDlg->fDone = true;
  503.             break;
  504. /*
  505.     Were the "All" or "From" page radio buttons pressed? Select them
  506.     appropriately.
  507. */
  508.         case kJob_AllPagesRadio:
  509.         case kJob_FromPageRadio:
  510.             GetDItem(theDialog, kJob_AllPagesRadio, &itemKind, &itemHdl, &itemRect);
  511.             SetTheControlValue(itemHdl, (item == kJob_AllPagesRadio)? 1: 0);
  512.             GetDItem(theDialog, kJob_FromPageRadio, &itemKind, &itemHdl, &itemRect);
  513.             SetTheControlValue(itemHdl, (item == kJob_FromPageRadio)? 1: 0);
  514.             break;
  515. /*
  516.     Were the "From" or "To" editText items hit?  Select the "From"
  517.     radio button, and deselect the "All" radio button.
  518. */
  519.         case kJob_FromPageEditText:
  520.         case kJob_ToPageEditText:
  521.             GetDItem(theDialog, kJob_AllPagesRadio, &itemKind, &itemHdl, &itemRect);
  522.             SetTheControlValue(itemHdl, 0);
  523.             GetDItem(theDialog, kJob_FromPageRadio, &itemKind, &itemHdl, &itemRect);
  524.             SetTheControlValue(itemHdl, 1);
  525.             break;
  526. /*
  527.     Were the "Automatic" or "Manual" feed radio buttons hit? Select
  528.     them appropriately.
  529. */
  530.         case kJob_AutoFeedRadio:
  531.         case kJob_ManualFeedRadio:
  532.             GetDItem(theDialog, kJob_AutoFeedRadio, &itemKind, &itemHdl, &itemRect);
  533.             SetTheControlValue(itemHdl, (item == kJob_AutoFeedRadio)? 1: 0);
  534.             GetDItem(theDialog, kJob_ManualFeedRadio, &itemKind, &itemHdl, &itemRect);
  535.             SetTheControlValue(itemHdl, (item == kJob_ManualFeedRadio)? 1: 0);
  536.             break;
  537. /*
  538.     Was the "Print to disk" checkBox hit?  Toggle its value.
  539. */
  540.         case kJob_PrintToDiskCheckBox:
  541.             GetDItem(theDialog, kJob_PrintToDiskCheckBox, &itemKind, &itemHdl, &itemRect);
  542.             ToggleControl(itemHdl);
  543.             break;
  544.     }
  545. }
  546.  
  547.  
  548. /*    ______________________________________________________________
  549.  
  550.     UpdateJobDlgPrintRec -
  551.     
  552.     This routine updates the job dialog's print record so that
  553.     it reflects the current dialog settings.  This routine is only
  554.     called when a user "OKs" the job dialog. It is the opposite of
  555.     InitJobDialogControls.
  556.  
  557.     WHY THIS IS IMPORTANT:
  558.     
  559.     This is how we transfer all the button clicks we handled in
  560.     DriverJobItemHandler into settings in the driver's print
  561.     record.
  562.  
  563.     ______________________________________________________________    */
  564.  
  565. void UpdateJobDlgPrintRec(DialogPtr theDialog)
  566. {
  567.     TPPrDlg        thePrDlg = (TPPrDlg) theDialog;
  568.     THPrint        printRecHdl = thePrDlg->hPrintUsr;
  569.     short        itemKind, ctlVal;
  570.     long        numCopies, fstPage, lstPage;
  571.     Handle        itemHdl;
  572.     Rect        itemRect;
  573.     Str255        pStr;
  574.  
  575. /*
  576.     Get the value of the "All pages" radio button.  If it's set,
  577.     make our page range 1 through 9999.  Otherwise, use the user's
  578.     range, (from the "From" and "To" editText fields.  Also do a
  579.     bit of sanity checking on the user's values.
  580. */
  581.  
  582.     GetDItem(theDialog, kJob_AllPagesRadio, &itemKind, &itemHdl, &itemRect);
  583.     ctlVal = GetCtlValue((ControlHandle) itemHdl);
  584.     if (ctlVal != 0)
  585.     {
  586.         (*printRecHdl)->prJob.iFstPage = 1;
  587.         (*printRecHdl)->prJob.iLstPage = 9999;
  588.     }
  589.     else
  590.     {
  591.         GetDItem(theDialog, kJob_FromPageEditText, &itemKind, &itemHdl, &itemRect);
  592.         GetIText(itemHdl, pStr);
  593.         StringToNum(pStr, &fstPage);
  594.         GetDItem(theDialog, kJob_ToPageEditText, &itemKind, &itemHdl, &itemRect);
  595.         GetIText(itemHdl, pStr);
  596.         StringToNum(pStr, &lstPage);
  597.         
  598.         fstPage = (fstPage < 1)? 1: fstPage;
  599.         fstPage = (fstPage > 9999)? 9999: fstPage;
  600.         lstPage = (lstPage < 1)? 1: lstPage;
  601.         lstPage = (lstPage > 9999)? 9999: lstPage;
  602.         lstPage = (lstPage < fstPage)? fstPage: lstPage;
  603.  
  604.         (*printRecHdl)->prJob.iFstPage = fstPage; 
  605.         (*printRecHdl)->prJob.iLstPage = lstPage; 
  606.     }
  607.  
  608. /*
  609.     Get the number of copies and store it in the print record
  610.     where our driver likes it.
  611. */
  612.  
  613.     GetDItem(theDialog, kJob_CopiesEditText, &itemKind, &itemHdl, &itemRect);
  614.     GetIText(itemHdl, pStr);
  615.     StringToNum(pStr, &numCopies);
  616.     (*printRecHdl)->prXInfo.iRowBytes = (numCopies > 0)? numCopies:1;
  617.  
  618.  
  619. /*
  620.     Get the "print to disk" value, and store it in the print record
  621.     where our driver likes it.  Yes, this looks a bit weird, but 120
  622.     bytes only go so far.
  623.     
  624.     Note that we disable manual feed if we're printing to disk.
  625.     If we're not printing to disk, see if we're manually feeding,
  626.     and store that info as our driver likes it.
  627. */
  628.  
  629.     GetDItem(theDialog, kJob_PrintToDiskCheckBox, &itemKind, &itemHdl, &itemRect);
  630.     ctlVal = GetCtlValue((ControlHandle) itemHdl);
  631.     (*printRecHdl)->prXInfo.bUlOffset = (ctlVal != 0)? 1 :0;
  632.  
  633.     if (ctlVal == 0)
  634.     {
  635.         GetDItem(theDialog, kJob_ManualFeedRadio, &itemKind, &itemHdl, &itemRect);
  636.         ctlVal = GetCtlValue((ControlHandle) itemHdl);
  637.         (*printRecHdl)->prStl.feed = (ctlVal != 0)? 3: 2;
  638.     }
  639.     else
  640.         (*printRecHdl)->prStl.feed = 2;
  641. }
  642.  
  643.  
  644. /*    ______________________________________________________________
  645.  
  646.     InitJobDialogControls -
  647.     
  648.     This routine initializes the controls of our dialog so that
  649.     they reflect what's in the passed print record.  This routine
  650.     is only called when the job dialog is about to be displayed.
  651.     It is the opposite of UpdateJobDlgPrintRec.
  652.  
  653.     WHY THIS IS IMPORTANT:
  654.     
  655.     This is where we synchronize the job dialog with what's in
  656.     our print record.
  657.  
  658.     ______________________________________________________________    */
  659.  
  660. void InitJobDialogControls(DialogPtr theDialog, THPrint thePrintHdl)
  661. {
  662.     short            itemKind;
  663.     Handle            itemHdl1, itemHdl2, itemHdl3, itemHdl4;
  664.     Rect            itemRect;
  665.     TPrint            *printRecPtr;
  666.     Str255            pStr;
  667.     char            oldState;
  668.  
  669. /*
  670.     Lock down the print handle so we can treat it as a pointer.
  671.     Save the lock state so that we can restore it when we exit.
  672. */
  673.     
  674.     oldState = HGetState((Handle) thePrintHdl);
  675.     HLock((Handle) thePrintHdl);
  676.     printRecPtr = *thePrintHdl;
  677.  
  678.  
  679. /*
  680.     Set up the "From… To…" editText items, and enable the appropriate
  681.     radio button.  We only enable the "All pages" radio button if
  682.     pages 1 through 9999 are selected.
  683. */
  684.     
  685.     GetDItem(theDialog, kJob_AllPagesRadio, &itemKind, &itemHdl1, &itemRect);
  686.     GetDItem(theDialog, kJob_FromPageRadio, &itemKind, &itemHdl2, &itemRect);
  687.     GetDItem(theDialog, kJob_FromPageEditText, &itemKind, &itemHdl3, &itemRect);
  688.     GetDItem(theDialog, kJob_ToPageEditText, &itemKind, &itemHdl4, &itemRect);
  689.     
  690.     if ((printRecPtr->prJob.iFstPage == 1) && (printRecPtr->prJob.iLstPage == 9999))
  691.     {
  692.         SetTheControlValue(itemHdl1, 1);        // Printing all pages
  693.         SetTheControlValue(itemHdl2, 0);
  694.         SetIText(itemHdl3, "\p");
  695.         SetIText(itemHdl4, "\p");
  696.     }
  697.     else
  698.     {
  699.         SetTheControlValue(itemHdl1, 0);        // Printing "From… To…"
  700.         SetTheControlValue(itemHdl2, 1);
  701.         NumToString(printRecPtr->prJob.iFstPage, pStr);
  702.         SetIText(itemHdl3, pStr);
  703.         NumToString(printRecPtr->prJob.iLstPage, pStr);
  704.         SetIText(itemHdl4, pStr);
  705.     }
  706.  
  707. /*
  708.     Set up the number of copies editText item.  Our driver stores this
  709.     value in the prXInfo.iRowBytes field of the print record.
  710. */
  711.     
  712.     GetDItem(theDialog, kJob_CopiesEditText, &itemKind, &itemHdl1, &itemRect);
  713.     NumToString(printRecPtr->prXInfo.iRowBytes, pStr);
  714.     SetIText(itemHdl1, pStr);
  715.  
  716.  
  717. /*
  718.     Set up the "Automatic" or "Manual" Feed radio buttons.  Our
  719.     driver stores this information in the prStl.feed field of
  720.     the print record.
  721. */
  722.     
  723.     GetDItem(theDialog, kJob_AutoFeedRadio, &itemKind, &itemHdl1, &itemRect);
  724.     SetTheControlValue(itemHdl1, (printRecPtr->prStl.feed == 3)? 0: 1);
  725.     GetDItem(theDialog, kJob_ManualFeedRadio, &itemKind, &itemHdl1, &itemRect);
  726.     SetTheControlValue(itemHdl1, (printRecPtr->prStl.feed == 3)? 1: 0);
  727.  
  728.  
  729. /*
  730.     Set up the "Print to disk" checkBox.  Our driver stores this
  731.     information in the prXInfo.bUlOffset field of the print record.
  732.     Restore the print handle's lock status as we exit this routine.
  733. */
  734.     
  735.     GetDItem(theDialog, kJob_PrintToDiskCheckBox, &itemKind, &itemHdl1, &itemRect);
  736.     SetTheControlValue(itemHdl1, (printRecPtr->prXInfo.bUlOffset == 0)? 0: 1);
  737.  
  738.     HSetState((Handle) thePrintHdl, oldState);
  739. }
  740.  
  741.  
  742. /*    ______________________________________________________________
  743.  
  744.     PrepForFileWriting -
  745.     
  746.     This routine asks the user where to save a PostScript file.
  747.     If the user doesn't cancel, it saves the appropriate
  748.     collection items in the current job to make printing to a
  749.     file happen.
  750.  
  751.     WHY THIS IS IMPORTANT:
  752.     
  753.     The default PostScript implementation of PrJobInit installs
  754.     an item handler proc for the default old-API dialogs.  The
  755.     "print to file" StandardFile dialog is handled in that item
  756.     handling proc.  Since we completely override gxPrJobInit, the
  757.     default "print to file" file dialog code is never used.  You
  758.     must supply this routine if you completely override PrJobInit,
  759.     and you want to print to PostScript (or other) files.
  760.     
  761.     For simplicity, this file dialog only saves in one format--
  762.     PostScript® with no fonts.  It's a straightforward
  763.     CustomPutFile exercise to reproduce the default dialog if you
  764.     so desire.
  765.  
  766.     ______________________________________________________________    */
  767.  
  768. OSErr PrepForFileWriting()
  769. {
  770.     OSErr                        err = noErr;
  771.     gxFileDestinationInfo        destInfo;
  772.     gxFileFormatInfo            destFFmt;
  773.     gxFileLocationInfo            fileLocInfo;
  774.     gxFileFontsInfo                fontsInfo;
  775.     StandardFileReply            putReply;
  776.  
  777. /*
  778.     Display a StandardPutFile dialog and get the location to
  779.     save to.
  780. */
  781.  
  782.     StandardPutFile("\pPostScript® file to create:",
  783.                     "\pUntitled Print File", &putReply);
  784.  
  785.     require_action(putReply.sfGood, GotAnError, err = gxPrUserAbortErr;);
  786.  
  787. /*
  788.     If the user didn't cancel, save the file location, the
  789.     print destination (to disk), the file format name, and
  790.     the font inclusion info.
  791. */
  792.  
  793.     BlockMove(&putReply.sfFile, &fileLocInfo.fileSpec, sizeof(FSSpec));
  794.     StoreJobCollectionItem(&fileLocInfo, sizeof(gxFileLocationInfo),
  795.                            gxFileLocationTag, gxPrintingTagID);
  796.  
  797.     destInfo.toFile = true;
  798.     StoreJobCollectionItem(&destInfo, sizeof(gxFileDestinationInfo),
  799.                            gxFileDestinationTag, gxPrintingTagID);
  800.  
  801.     BlockMove("PostScript®", &destFFmt.fileFormatName[1], 11);
  802.     destFFmt.fileFormatName[0] = 11;
  803.     StoreJobCollectionItem(&destFFmt, sizeof(gxFileFormatInfo),
  804.                            gxFileFormatTag, gxPrintingTagID);
  805.  
  806.     fontsInfo.includeFonts = gxIncludeNoFonts;
  807.     StoreJobCollectionItem(&fontsInfo, sizeof(gxFileFontsInfo),
  808.                            gxFileFontsTag, gxPrintingTagID);
  809. GotAnError:
  810.  
  811.     return err;
  812. }
  813.  
  814.  
  815. /*    ______________________________________________________________
  816.  
  817.     DriverPrValidate -
  818.     
  819.     This routine is a total override of the gxPrValidate message.
  820.     In here, we set up our default settings if we have an
  821.     uninitialized or unknown print record.
  822.  
  823.     WHY THIS IS IMPORTANT:
  824.     
  825.     If you don't override this message, the default PostScript
  826.     implementation will validate your print records for you.  If
  827.     you're using a different wDev, then the default implementation
  828.     will keep changing your print record as soon as it notices
  829.     your wDev.  You don't need to completely override this message,
  830.     but if your wDev is different than the LaserWriter driver's
  831.     you'll have to massage it before and after you forward the
  832.     message.
  833.  
  834.     The implemenation below assumes that we've overriden
  835.     gxPrintDefault so that our default print record is read in.
  836.  
  837.     ______________________________________________________________    */
  838.  
  839. OSErr DriverPrValidate(THPrint printHdl, Boolean *changedPtr)
  840. {
  841.     short        deviceWord, version;
  842.     Boolean        changed = true;
  843.     OSErr        err = noErr;
  844.  
  845. /*
  846.     If the high byte of the wDev for our driver and this print
  847.     record don't agree, call PrintDefault to load our default
  848.     print record.  Note that we don't check the version number
  849.     here, but you may want to do that as well.
  850.     
  851.     If the caller didn't pass us nil, return a flag indicating
  852.     whether or not we changed the print record.
  853. */
  854.  
  855.     deviceWord = (*printHdl)->prStl.wDev;
  856.     version = (*printHdl)->iPrVersion;
  857.     
  858.     if ((deviceWord >> 8) != kDeviceByte)
  859.         PrintDefault(printHdl);
  860.     else
  861.         changed = false;
  862.  
  863.     if (changedPtr != nil)
  864.         *changedPtr = changed;
  865.     
  866.     return err;
  867. }
  868.  
  869.  
  870. /*    ______________________________________________________________
  871.  
  872.     DriverPrintDefault -
  873.     
  874.     This routine is a total override of the gxPrintDefault message.
  875.     In here, we copy our default settings into the passed print
  876.     handle.
  877.  
  878.     WHY THIS IS IMPORTANT:
  879.     
  880.     You need to override this message so that your gxPrValidate
  881.     override and other callers can get your driver's default print
  882.     record when they need it.  This is best handled as a total
  883.     override, since the driver writer should specify what the
  884.     driver's default values are.
  885.  
  886.     ______________________________________________________________    */
  887.  
  888. OSErr DriverPrintDefault(THPrint printHdl)
  889. {
  890.     OSErr    err = noErr;
  891.     short    oldResFile;
  892.     Handle    defaultPrintRec;
  893.  
  894. /*
  895.     Save our old resource file, use our driver's resource file,
  896.     and load our default 'PREC' resource.  Copy that data into
  897.     our print handle, release the resource, and restore the
  898.     original resource file to the top of the chain before exiting.
  899. */
  900.  
  901.     oldResFile = CurResFile();
  902.     UseResFile(GXGetMessageHandlerResFile());
  903.  
  904.     defaultPrintRec = GetResource('PREC', 0);
  905.     require_action(defaultPrintRec, GetResource_Failed, err = resNotFound;);
  906.  
  907.     BlockMove(*defaultPrintRec, *printHdl, GetHandleSize(defaultPrintRec));
  908.     ReleaseResource(defaultPrintRec);
  909.  
  910. GetResource_Failed:
  911.     
  912.     UseResFile(oldResFile);
  913.     
  914.     return err;
  915. }
  916.  
  917. /*    ______________________________________________________________ */
  918.  
  919. enum {        // wdev
  920.     kFontSubstitution     = 0x0001,        
  921.     kPortrait             = 0x0002,
  922.     kGraphicSmoothing    = 0x0004,    
  923.     kTextSmoothing        = 0x0020
  924. };
  925.  
  926. enum {        // printX[0]
  927.     kColorMode             = 0x0008        
  928. };
  929.  
  930. enum {        // printX[5]
  931.     kInvertPage         = 0x0001,        
  932.     kFlipPageHoriz        = 0x0002,
  933.     kFlipPageVert        = 0x0004,    
  934.     kPreciseBitmap        = 0x0010,
  935.     kLargerPrintArea    = 0x0020,
  936.     kDownloadFonts        = 0x0080,
  937.     kPlus90                = 0x1000
  938. };
  939.  
  940. /*    ______________________________________________________________
  941.  
  942.     DriverConvertPrintRecordTo -
  943.     
  944.     This routine is a total override of the gxConvertPrintRecordTo
  945.     message. In here, we translate the passed old-world print
  946.     record into a universal print record.  This routine is the
  947.     opposite of DriverConvertPrintRecordFrom.
  948.  
  949.     WHY THIS IS IMPORTANT:
  950.     
  951.     This is how we pass data between the old and new printing
  952.     architectures.  You normally totally override this message
  953.     since you should be the authority on where your driver stores
  954.     which settings.
  955.  
  956.     ______________________________________________________________    */
  957.  
  958. OSErr DriverConvertPrintRecordTo(THPrint aPrintHdl)
  959. {
  960.     gxUniversalPrintRecord        *univRecPtr;
  961.     TPrint                        *printRecPtr;
  962.  
  963. /*
  964.     This driver's print record already matches up pretty
  965.     well to the universal print record.  Only the fields
  966.     below need to be mapped.
  967. */
  968.     
  969.     univRecPtr = *(gxUniversalPrintRecordHdl) aPrintHdl;
  970.     printRecPtr = *aPrintHdl;
  971.  
  972.  
  973. /*
  974.     We move the orientation, "print to file" and "manual feed"
  975.     settings from their private locations to their universal
  976.     locations.  Note that we also update our devType, because
  977.     universal print records always have $A9 in the high byte
  978.     of their wDev.
  979. */
  980.     
  981.     univRecPtr->orientation = (printRecPtr->prStl.wDev & 0x2)? gxPortraitOrientation :gxLandscapeOrientation;
  982.     univRecPtr->saveFile  = (printRecPtr->prXInfo.bUlOffset == 0)? 0 :1;
  983.  
  984.     univRecPtr->feed = (printRecPtr->prStl.feed == 3)?  gxManualFeed :gxAutoFeed;
  985.     univRecPtr->devType = (univRecPtr->devType & 0xFF) | 0xA900;
  986.     univRecPtr->coverPage = printRecPtr->prXInfo.iBandV;
  987.  
  988.     univRecPtr->options = 0;
  989.  
  990.     if (printRecPtr->prStl.wDev & kFontSubstitution) 
  991.         univRecPtr->options |= gxFontSubstitution;    // font substitution
  992.     if (printRecPtr->prStl.wDev & kGraphicSmoothing) 
  993.         univRecPtr->options |= gxGraphicSmoothing;    // graphic smoothing (LW)
  994.     if (printRecPtr->prStl.wDev & kTextSmoothing) 
  995.         univRecPtr->options |= gxTextSmoothing;        // text smoothing (SC)
  996.  
  997.     if (printRecPtr->printX[0] & kColorMode) 
  998.         univRecPtr->options |= gxColorMode;            // color printing
  999.  
  1000.     if (printRecPtr->printX[5] & kInvertPage) 
  1001.         univRecPtr->options |= gxInvertPage;        // b/w invert image;
  1002.     if (printRecPtr->printX[5] & kFlipPageHoriz) 
  1003.         univRecPtr->options |= gxFlipPageHoriz;        // flip horizontal
  1004.     if (printRecPtr->printX[5] & kFlipPageVert) 
  1005.         univRecPtr->options |= gxFlipPageVert;        // flip vertical
  1006.     if (printRecPtr->printX[5] & kPreciseBitmap) 
  1007.         univRecPtr->options |= gxPreciseBitmap;        // tall adjusted (IW), precise bitmap (LW, SC)
  1008.     if (printRecPtr->printX[5] & kLargerPrintArea) 
  1009.         univRecPtr->options |= gxBiggerPages;        // no gaps (IW), larger print area (LW)
  1010.  
  1011.     if (false) 
  1012.         univRecPtr->options |= gxBidirectional;        // bidirectional printing
  1013.     if (false) 
  1014.         univRecPtr->options |= gxUserFlag2;            // user flag 2
  1015.  
  1016.     return noErr;
  1017. }
  1018.  
  1019.  
  1020. /*    ______________________________________________________________
  1021.  
  1022.     DriverConvertPrintRecordFrom -
  1023.     
  1024.     This routine is a total override of the gxConvertPrintRecordFrom
  1025.     message. In here, we translate the passed universal print record
  1026.     into an old-world print record.  This routine is the opposite of
  1027.     DriverConvertPrintRecordTo.
  1028.  
  1029.     WHY THIS IS IMPORTANT:
  1030.     
  1031.     This is how we pass data between the old and new printing
  1032.     architectures.  You normally totally override this message
  1033.     since you should be the authority on where your driver stores
  1034.     which settings.
  1035.  
  1036.     ______________________________________________________________    */
  1037.  
  1038. OSErr DriverConvertPrintRecordFrom(THPrint aPrintHdl)
  1039. {
  1040.     gxUniversalPrintRecord        *univRecPtr;
  1041.     TPrint                        *printRecPtr;
  1042.  
  1043. /*
  1044.     This driver's print record already matches up pretty
  1045.     well to the universal print record.  Only the fields
  1046.     below need to be mapped back to the old style print
  1047.     record.
  1048. */
  1049.     
  1050.     univRecPtr = *(gxUniversalPrintRecordHdl) aPrintHdl;
  1051.     printRecPtr = *aPrintHdl;
  1052.  
  1053. /*
  1054.     We move the orientation, "print to file" and "manual feed"
  1055.     settings from their universal locations to their private
  1056.     ones.  Note that we also update our WDev, because
  1057.     universal print records always have $A9 in the high byte
  1058.     of their wDev, and we need our device ID there.
  1059. */
  1060.  
  1061.     if (univRecPtr->orientation == gxPortraitOrientation)
  1062.         printRecPtr->prStl.wDev |= 0x2;
  1063.     else
  1064.         printRecPtr->prStl.wDev &= ~0x2;
  1065.  
  1066.     printRecPtr->prStl.feed = (univRecPtr->feed == gxManualFeed)? 3: 2;
  1067.     printRecPtr->prXInfo.bUlOffset = (univRecPtr->saveFile)? 1: 0;
  1068.  
  1069.     if (univRecPtr->options & gxFontSubstitution)    // font substitution
  1070.         printRecPtr->prStl.wDev |= kFontSubstitution;
  1071.     if (univRecPtr->options & gxGraphicSmoothing)    // graphic smoothing (LW)
  1072.         printRecPtr->prStl.wDev |= kGraphicSmoothing;
  1073.     if (univRecPtr->options & gxTextSmoothing)        // text smoothing (SC)
  1074.         printRecPtr->prStl.wDev |= kTextSmoothing;
  1075.  
  1076.     printRecPtr->prStl.wDev = (printRecPtr->prStl.wDev & 0xFF) | (kDeviceByte << 8);
  1077.                 
  1078.     if (univRecPtr->options & gxInvertPage)            // b/w invert image;
  1079.         printRecPtr->printX[5] |= kInvertPage;
  1080.     if (univRecPtr->options & gxFlipPageHoriz)        // flip horizontal
  1081.         printRecPtr->printX[5] |= kFlipPageHoriz;
  1082.     if (univRecPtr->options & gxFlipPageVert)        // flip vertical
  1083.         printRecPtr->printX[5] |= kFlipPageVert;
  1084.     if (univRecPtr->options & gxPreciseBitmap)        // tall adjusted (IW), precise bitmap (LW, SC)
  1085.         printRecPtr->printX[5] |= kPreciseBitmap;
  1086.     if (univRecPtr->options & gxBiggerPages)        // no gaps (IW), larger print area (LW)
  1087.         printRecPtr->printX[5] |= kLargerPrintArea;
  1088.  
  1089.     printRecPtr->prXInfo.iBandV = univRecPtr->coverPage;    
  1090.  
  1091. /*
  1092.     explicitly need to set fields in prXinfo part of univ to 0 or they will affect printrec,
  1093.     the following assumes that we don't ever care about whats in bPatScale, bUITThick, bUIOffset, etc
  1094. */
  1095.     univRecPtr->orientation = 0;                    // so iDevBytes doesn't get set
  1096.     univRecPtr->qualityMode = 0;
  1097.     univRecPtr->coverPage     = 0;                    // so iBands doesn't gets set to 1
  1098.     univRecPtr->firstTray     = 1;                    // as thats what LW does.
  1099.  
  1100.     return noErr;
  1101. }
  1102.  
  1103.  
  1104. /*    ______________________________________________________________
  1105.  
  1106.     StoreJobCollectionItem -
  1107.     
  1108.     This routine is a generic routine that stores a collection
  1109.     item in the current job's collection.  If the item already
  1110.     exists it is replaced.
  1111.  
  1112.     WHY THIS IS IMPORTANT:
  1113.     
  1114.     It's not.  This is just a utility routine.
  1115.  
  1116.     ______________________________________________________________    */
  1117.  
  1118. OSErr StoreJobCollectionItem(void *collectItem, long collectSize,
  1119.                              OSType collectType, long collectID)
  1120. {
  1121.     OSErr            err;
  1122.     Collection        jobCollection;
  1123.     long            index, itemSize, attributes;
  1124.  
  1125. /*
  1126.     Get the job collection and add the new data.  If the item
  1127.     already exists and is locked, replace it.
  1128. */
  1129.  
  1130.     jobCollection = GXGetJobCollection(GXGetJob());
  1131.  
  1132.     err = AddCollectionItem(jobCollection,
  1133.                             collectType,
  1134.                             collectID,
  1135.                             collectSize,
  1136.                             collectItem);
  1137.  
  1138.     if (err == collectionItemLockedErr)
  1139.     {
  1140.         err = GetCollectionItemInfo(jobCollection,
  1141.                                     collectType,
  1142.                                     collectID,
  1143.                                     &index,
  1144.                                     &itemSize,
  1145.                                     &attributes);
  1146.         if (!err)
  1147.             err = ReplaceIndexedCollectionItem(jobCollection,
  1148.                                                index,
  1149.                                                collectSize,
  1150.                                                collectItem);
  1151.     }
  1152.  
  1153.     return err;
  1154. }
  1155.  
  1156.  
  1157. /*    ______________________________________________________________
  1158.  
  1159.     ToggleControl -
  1160.     
  1161.     This routine toggles a control on or off.  It's useful for
  1162.     controls like checkboxes or radio buttons, which can only have
  1163.     a value of 0 or 1.  The function takes a "void **" so that we
  1164.     don't have to typecast the handle returned from GetDItem before
  1165.     calling this routine.
  1166.  
  1167.     WHY THIS IS IMPORTANT:
  1168.     
  1169.     It's not.  This is just a utility routine.
  1170.  
  1171.     ______________________________________________________________    */
  1172.  
  1173. void ToggleControl(void **itemH)
  1174. {
  1175.     short ctlVal = GetCtlValue((ControlHandle) itemH);
  1176.  
  1177.     ctlVal = (ctlVal == 1)? 0: 1;
  1178.     SetCtlValue((ControlHandle) itemH, ctlVal);
  1179. }
  1180.  
  1181.  
  1182. /*    ______________________________________________________________
  1183.  
  1184.     SetTheControlValue -
  1185.     
  1186.     This routine sets the passed control's value, if necessary.  It
  1187.     will not set a control to a value it's already set to, and that
  1188.     avoids flashing.  The function takes a "void **" so that we
  1189.     don't have to typecast the handle returned from GetDItem before
  1190.     calling this routine.
  1191.  
  1192.     WHY THIS IS IMPORTANT:
  1193.     
  1194.     It's not.  This is just a utility routine.
  1195.  
  1196.     ______________________________________________________________    */
  1197.  
  1198. void SetTheControlValue(void **itemH, short desiredValue)
  1199. {
  1200.     short ctlVal = GetCtlValue((ControlHandle) itemH);
  1201.  
  1202.     if (ctlVal != desiredValue)
  1203.         SetCtlValue((ControlHandle) itemH, desiredValue);
  1204. }
  1205.